`timescale 1ns / 1ps

`include "data_width.vh"

module rd_src_p_bram_block #(parameter
    VERTEX_PIPE_NUM     = `VERTEX_PIPE_NUM,
    VERTEX_BRAM_NUM     = `VERTEX_BRAM_NUM,
    VERTEX_BRAM_AWIDTH  = `VERTEX_BRAM_AWIDTH,
    VERTEX_BRAM_DWIDTH  = `VERTEX_BRAM_DWIDTH,
    REORDER_LOC_Y_WIDTH = `REORDER_LOC_Y_WIDTH,
    TOT_EDGE_MASK_WIDTH = `TOT_EDGE_MASK_WIDTH,
    TOT_ACC_ID_WIDTH    = `TOT_ACC_ID_WIDTH,
    DST_ID_DWIDTH       = `DST_ID_DWIDTH,
    VERTEX_MASK_WIDTH   = `VERTEX_MASK_WIDTH,
    WB_VALID_WIDTH      = `WB_VALID_WIDTH
    ) (
        input                                                   clk,
        input                                                   front_rst,
        input [VERTEX_BRAM_AWIDTH * VERTEX_BRAM_NUM - 1 : 0]    front_rd_src_p_addr,
        input [REORDER_LOC_Y_WIDTH * VERTEX_BRAM_NUM - 1 : 0]   front_reorder_loc_y,
        input [VERTEX_BRAM_NUM - 1 : 0]                         front_rd_src_p_valid,
        input                                                   front_rd_src_p_addr_finish,
        input [TOT_EDGE_MASK_WIDTH - 1 : 0]                     front_tot_src_p_mask,
        input [TOT_ACC_ID_WIDTH - 1 : 0]                        front_tot_acc_id,
        input                                                   front_any_dst_data_valid,
        input [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]         front_dst_id,
        input [VERTEX_MASK_WIDTH * VERTEX_PIPE_NUM - 1 : 0]     front_src_p_mask_r,
        input [VERTEX_PIPE_NUM - 1 : 0]                         front_dst_data_valid,
        input                                                   back_stage_vertex_full,
        input [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]         wb_dst_addr,
        input [VERTEX_BRAM_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]    wb_dst_p,
        input [WB_VALID_WIDTH * VERTEX_PIPE_NUM - 1 : 0]        wb_dst_data_valid,

        output                                                  rst,
        output                                                  buffer_full_vertex,
        output [VERTEX_BRAM_DWIDTH * VERTEX_BRAM_NUM - 1 : 0]   src_p,
        output [REORDER_LOC_Y_WIDTH * VERTEX_BRAM_NUM - 1 : 0]  reorder_loc_y,
        output [VERTEX_BRAM_NUM - 1 : 0]                        src_p_valid,
        output                                                  rd_src_p_addr_finish,
        output [TOT_EDGE_MASK_WIDTH - 1 : 0]                    tot_src_p_mask,
        output [TOT_ACC_ID_WIDTH - 1 : 0]                       tot_acc_id,
        output                                                  any_dst_data_valid,
        output [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]        dst_id,
        output [VERTEX_MASK_WIDTH * VERTEX_PIPE_NUM - 1 : 0]    src_p_mask_r,
        output [VERTEX_PIPE_NUM - 1 : 0]                        dst_data_valid);

    wire                            bram_buffer_empty, bram_buffer_full;
    wire                            mask_buffer_empty, mask_buffer_full;
    wire [VERTEX_PIPE_NUM - 1 : 0]  dst_buffer_empty, dst_buffer_full;
    wire [VERTEX_PIPE_NUM - 1 : 0]  data_valid;

    assign buffer_full_vertex = dst_buffer_full[0];
    assign any_dst_data_valid = data_valid[0];

    rd_src_p_bram_block_para_trans P (
        .clk(clk), .front_rst(front_rst),

        .rst(rst));

    rd_src_p_bram_block_bram E1 (
        .clk(clk), .rst(front_rst),
        .front_rd_src_p_addr(front_rd_src_p_addr), .front_reorder_loc_y(front_reorder_loc_y), .front_rd_src_p_valid(front_rd_src_p_valid),
        .front_rd_src_p_addr_finish(front_rd_src_p_addr_finish),
        .wb_dst_addr(wb_dst_addr), .wb_dst_p(wb_dst_p), .wb_dst_data_valid(wb_dst_data_valid),

        // .buffer_empty(bram_buffer_empty), .buffer_full(bram_buffer_full),
        .src_p(src_p), .reorder_loc_y(reorder_loc_y), .src_p_valid(src_p_valid),
        .rd_src_p_addr_finish(rd_src_p_addr_finish));

    rd_src_p_bram_block_mask M1 (
        .clk(clk), .rst(front_rst),
        .front_tot_src_p_mask(front_tot_src_p_mask), .front_tot_acc_id(front_tot_acc_id),
        .front_any_dst_data_valid(front_any_dst_data_valid), .back_stage_vertex_full(back_stage_vertex_full),

        .buffer_empty(mask_buffer_empty), .buffer_full(mask_buffer_full),
        .tot_src_p_mask(tot_src_p_mask), .tot_acc_id(tot_acc_id));

    generate
        genvar i;
        for (i = 0; i < VERTEX_PIPE_NUM; i = i + 1) begin : M10_BLOCK_1
            rd_src_p_bram_block_vertex_single V (
                .clk                        (clk),
                .rst                        (front_rst),
                .front_dst_id               (front_dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .front_src_p_mask_r         (front_src_p_mask_r[(i + 1) * VERTEX_MASK_WIDTH - 1 : i * VERTEX_MASK_WIDTH]),
                .front_dst_data_valid       (front_dst_data_valid[i]),
                .front_any_dst_data_valid   (front_any_dst_data_valid),
                .back_stage_vertex_full     (back_stage_vertex_full),

                .buffer_empty               (dst_buffer_empty[i]),
                .buffer_full                (dst_buffer_full[i]),
                .data_valid                 (data_valid[i]),
                .dst_id                     (dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .src_p_mask_r               (src_p_mask_r[(i + 1) * VERTEX_MASK_WIDTH - 1 : i * VERTEX_MASK_WIDTH]),
                .dst_data_valid             (dst_data_valid[i]));
        end
    endgenerate

endmodule

module rd_src_p_bram_block_para_trans (
    input       clk,
    input       front_rst,

    output reg  rst);

    always @ (posedge clk) begin
        rst <= front_rst;
    end

endmodule

module rd_src_p_bram_block_bram #(parameter
    VERTEX_BRAM_NUM         = `VERTEX_BRAM_NUM,
    VERTEX_BRAM_AWIDTH      = `VERTEX_BRAM_AWIDTH,
    VERTEX_BRAM_DWIDTH      = `VERTEX_BRAM_DWIDTH,
    VERTEX_BRAM_NUM_WIDTH   = `VERTEX_BRAM_NUM_WIDTH,
    DST_ID_DWIDTH           = `DST_ID_DWIDTH,
    REORDER_LOC_Y_WIDTH     = `REORDER_LOC_Y_WIDTH,
    VERTEX_PIPE_NUM         = `VERTEX_PIPE_NUM,
    WB_VALID_WIDTH          = `WB_VALID_WIDTH
    ) (
        input                                                       clk,
        input                                                       rst,
        input [VERTEX_BRAM_AWIDTH * VERTEX_BRAM_NUM - 1 : 0]        front_rd_src_p_addr,
        input [REORDER_LOC_Y_WIDTH * VERTEX_BRAM_NUM - 1 : 0]       front_reorder_loc_y,
        input [VERTEX_BRAM_NUM - 1 : 0]                             front_rd_src_p_valid,
        input                                                       front_rd_src_p_addr_finish,
        input [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]             wb_dst_addr,
        input [VERTEX_BRAM_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]        wb_dst_p,
        input [WB_VALID_WIDTH * VERTEX_PIPE_NUM - 1 : 0]            wb_dst_data_valid,

        output reg [VERTEX_BRAM_DWIDTH * VERTEX_BRAM_NUM - 1 : 0]   src_p,
        output reg [REORDER_LOC_Y_WIDTH * VERTEX_BRAM_NUM - 1 : 0]  reorder_loc_y,
        output reg [VERTEX_BRAM_NUM - 1 : 0]                        src_p_valid,
        output reg                                                  rd_src_p_addr_finish);

    reg [REORDER_LOC_Y_WIDTH * VERTEX_BRAM_NUM - 1 : 0] reorder_loc_y_buffer_1;
    reg [REORDER_LOC_Y_WIDTH * VERTEX_BRAM_NUM - 1 : 0] reorder_loc_y_buffer_2;
    reg [REORDER_LOC_Y_WIDTH * VERTEX_BRAM_NUM - 1 : 0] reorder_loc_y_buffer_3;
    reg [VERTEX_BRAM_NUM - 1 : 0]                       src_p_valid_buffer_1;
    reg [VERTEX_BRAM_NUM - 1 : 0]                       src_p_valid_buffer_2;
    reg [VERTEX_BRAM_NUM - 1 : 0]                       src_p_valid_buffer_3;
    reg                                                 rd_src_p_addr_finish_buffer_1;
    reg                                                 rd_src_p_addr_finish_buffer_2;
    reg                                                 rd_src_p_addr_finish_buffer_3;
    // buff address and data before access bram
    (* dont_touch = "yes" *) reg                                                 rst_buffer_in; // buffer reset
    (* dont_touch = "yes" *) reg [VERTEX_BRAM_AWIDTH * VERTEX_BRAM_NUM - 1 : 0]  rd_src_p_addr_buffer_in;
    (* dont_touch = "yes" *) reg [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]       wb_dst_addr_buffer_in;
    (* dont_touch = "yes" *) reg [VERTEX_BRAM_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]  wb_dst_p_buffer_in;
    (* dont_touch = "yes" *) reg [WB_VALID_WIDTH * VERTEX_PIPE_NUM - 1 : 0]      wb_dst_data_valid_buffer_in;
    // buff data after access bram.
    wire [VERTEX_BRAM_DWIDTH * VERTEX_BRAM_NUM - 1 : 0]  src_p_buffer_out;

    always @ (posedge clk) begin
        rst_buffer_in <= rst;
    end

    // 非 bram 信息延时
    always @ (posedge clk) begin
        if (rst) begin
            reorder_loc_y_buffer_1          <= 0;
            reorder_loc_y_buffer_2          <= 0;
            reorder_loc_y_buffer_3          <= 0;
            src_p_valid_buffer_1            <= 0;
            src_p_valid_buffer_2            <= 0;
            src_p_valid_buffer_3            <= 0;
            rd_src_p_addr_finish_buffer_1   <= 0;
            rd_src_p_addr_finish_buffer_2   <= 0;
            rd_src_p_addr_finish_buffer_3   <= 0;

        end
        else begin
            // 1
            reorder_loc_y_buffer_1          <= front_reorder_loc_y;
            src_p_valid_buffer_1            <= front_rd_src_p_valid;
            rd_src_p_addr_finish_buffer_1   <= front_rd_src_p_addr_finish;
            // 2
            reorder_loc_y_buffer_2          <= reorder_loc_y_buffer_1;
            src_p_valid_buffer_2            <= src_p_valid_buffer_1;
            rd_src_p_addr_finish_buffer_2   <= rd_src_p_addr_finish_buffer_1;
            // 3
            reorder_loc_y_buffer_3          <= reorder_loc_y_buffer_2;
            src_p_valid_buffer_3            <= src_p_valid_buffer_2;
            rd_src_p_addr_finish_buffer_3   <= rd_src_p_addr_finish_buffer_2;
        end
    end

    // bram 信息延时
    always @ (posedge clk) begin
        if (rst) begin
            rd_src_p_addr_buffer_in     <= 0;
            wb_dst_addr_buffer_in       <= 0;
            wb_dst_p_buffer_in          <= 0;
            wb_dst_data_valid_buffer_in <= 0;
        end
        else begin
            rd_src_p_addr_buffer_in     <= front_rd_src_p_addr;
            wb_dst_addr_buffer_in       <= wb_dst_addr;
            wb_dst_p_buffer_in          <= wb_dst_p;
            wb_dst_data_valid_buffer_in <= wb_dst_data_valid;
        end
    end

    // output
    always @ (posedge clk) begin
        if (rst) begin
            src_p                   <= 0;
            reorder_loc_y           <= 0;
            src_p_valid             <= 0;
            rd_src_p_addr_finish    <= 1'b0;
        end
        else begin
            src_p                   <= src_p_buffer_out;
            reorder_loc_y           <= reorder_loc_y_buffer_3;
            src_p_valid             <= src_p_valid_buffer_3;
            rd_src_p_addr_finish    <= rd_src_p_addr_finish_buffer_3;
        end
    end

    // 第 0 块 bram 的第 0 号地址初始值为 0
    vertex_bram_zero BRAM_0 (
        .clka   (clk),
        .clkb   (clk),
        .ena    (1'b1),
        .enb    (1'b1),
        .addra  (wb_dst_addr_buffer_in[VERTEX_BRAM_AWIDTH + VERTEX_BRAM_NUM_WIDTH - 1 : VERTEX_BRAM_NUM_WIDTH]),
        .dina   (wb_dst_p_buffer_in[VERTEX_BRAM_DWIDTH - 1 : 0]),
        .wea    (wb_dst_data_valid_buffer_in[0]),
        .addrb  (rd_src_p_addr_buffer_in[VERTEX_BRAM_AWIDTH - 1 : 0]),

        .doutb  (src_p_buffer_out[VERTEX_BRAM_DWIDTH - 1 : 0]));

    // 第 1 - 63 块 bram
    generate
        genvar i;
        for (i = 1; i < VERTEX_BRAM_NUM; i = i + 1) begin: M10_BLOCK_2
            vertex_bram BRAM (
                .clka   (clk),
                .clkb   (clk),
                .ena    (1'b1),
                .enb    (1'b1),
                .addra  (wb_dst_addr_buffer_in[(i % VERTEX_PIPE_NUM) * DST_ID_DWIDTH + VERTEX_BRAM_AWIDTH + VERTEX_BRAM_NUM_WIDTH - 1 : (i % VERTEX_PIPE_NUM) * DST_ID_DWIDTH + VERTEX_BRAM_NUM_WIDTH]),
                .dina   (wb_dst_p_buffer_in[(i % VERTEX_PIPE_NUM + 1) * VERTEX_BRAM_DWIDTH - 1 : (i % VERTEX_PIPE_NUM) * VERTEX_BRAM_DWIDTH]),
                .wea    (wb_dst_data_valid_buffer_in[(i % VERTEX_PIPE_NUM) * WB_VALID_WIDTH + (i / VERTEX_PIPE_NUM)]),
                .addrb  (rd_src_p_addr_buffer_in[(i + 1) * VERTEX_BRAM_AWIDTH - 1 : i * VERTEX_BRAM_AWIDTH]),

                .doutb  (src_p_buffer_out[(i + 1) * VERTEX_BRAM_DWIDTH - 1 : i * VERTEX_BRAM_DWIDTH]));
        end
    endgenerate

endmodule

module rd_src_p_bram_block_mask #(parameter
    TOT_EDGE_MASK_WIDTH = `TOT_EDGE_MASK_WIDTH, TOT_ACC_ID_WIDTH = `TOT_ACC_ID_WIDTH
    ) (
        input                                   clk,
        input                                   rst,
        input [TOT_EDGE_MASK_WIDTH - 1 : 0]     front_tot_src_p_mask,
        input [TOT_ACC_ID_WIDTH - 1 : 0]        front_tot_acc_id,
        input                                   front_any_dst_data_valid,
        input                                   back_stage_vertex_full,

        output                                  buffer_empty,
        output                                  buffer_full,
        output [TOT_EDGE_MASK_WIDTH - 1 : 0]    tot_src_p_mask,
        output [TOT_ACC_ID_WIDTH - 1 : 0]       tot_acc_id);

    tot_edge_mask_fifo TEM1 (
        .clk(clk), .srst(rst),
        .din(front_tot_src_p_mask), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(tot_src_p_mask), .empty(buffer_empty), .prog_full(buffer_full));

    tot_acc_id_fifo TAI1 (
        .clk(clk), .srst(rst),
        .din(front_tot_acc_id), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(tot_acc_id));

endmodule

module rd_src_p_bram_block_vertex_single #(parameter
    DST_ID_DWIDTH       = `DST_ID_DWIDTH,
    VERTEX_MASK_WIDTH   = `VERTEX_MASK_WIDTH
    ) (
        input                                   clk,
        input                                   rst,
        input [DST_ID_DWIDTH - 1 : 0]           front_dst_id,
        input [VERTEX_MASK_WIDTH - 1 : 0]       front_src_p_mask_r,
        input                                   front_dst_data_valid,
        input                                   front_any_dst_data_valid,
        input                                   back_stage_vertex_full,

        output                                  buffer_empty,
        output                                  buffer_full,
        output                                  data_valid,
        output [DST_ID_DWIDTH - 1 : 0]          dst_id,
        output [VERTEX_MASK_WIDTH - 1 : 0]      src_p_mask_r,
        output                                  dst_data_valid);

    dst_id_fifo DI1 (
        .clk(clk), .srst(rst),
        .din(front_dst_id), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(dst_id), .empty(buffer_empty), .prog_full(buffer_full));

    vertex_mask_fifo VM1 (
        .clk(clk), .srst(rst),
        .din(front_src_p_mask_r), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(src_p_mask_r));

    valid_fifo DDV1 (
        .clk(clk), .srst(rst),
        .din(front_dst_data_valid), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(dst_data_valid), .valid(data_valid));

endmodule